<?php

/**
 * 红黑大战
 */
class HongHei {

    /**
     * 花色：0:spade(黑桃); 1:heart(红心); 2:club(梅花); 3:diamond(菱形)
     * @var array
     */
    protected $colors = array(
        'spade' => 0,
        'heart' => 1,
        'club' => 2,
        'diamond' => 3,
    );

    /**
     * 牌点：0:2; 1:3; 2:4; 3:5; 4:6; 5:7; 6:8; 7:9; 8:10; 9:J; 10:Q; 11:K; 12:A
     * @var array
     */
    protected $figures = array(
        '2' => 0,
        '3' => 1,
        '4' => 2,
        '5' => 3,
        '6' => 4,
        '7' => 5,
        '8' => 6,
        '9' => 7,
        '10' => 8,
        'J' => 9,
        'Q' => 10,
        'K' => 11,
        'A' => 12,
    );

    /**
     * 牌型
     * @var array
     */
    protected $type = array(
        'bz' => 0, // 豹子(三条)
        'ths' => 1, // 同花顺
        'th' => 2, // 同花
        'sz' => 3, // 顺子
        'dz' => 4, // 对子
        'dp' => 5, // 单牌
    );

    /**
     * 手牌(花色名称,牌点名称)['heart', 'Q']
     * @var array
     */
    protected $cards;

    /**
     * 洗牌(花色名称,牌点名称)['heart', 'Q']
     */
    public function shuffleCards() {
        $cards = array();
        foreach ($this->colors as $color => $value) {
            foreach ($this->figures as $figure => $value2) {
                $cards[] = array($color, $figure);
            }
        }
        shuffle($cards);
        $this->cards = $cards;
    }

    /**
     * 判断牌的种类
     * @param $cards
     * @return array compact('type', 'figures', 'colors','typecolor','typefigure')
     */
    public function checkCards($cards) {
        // 冒泡排序(把传入的n张卡牌按冒泡排序整理好)
        $max = sizeof($cards) - 1;
        for ($j = 0; $j < $max; $j++) {
            for ($i = 0; $i < $max - $j; $i++) {
                if ($this->figures[$cards[$i][1]] < $this->figures[$cards[$i + 1][1]]) {
                    $card = $cards[$i];
                    $cards[$i] = $cards[$i + 1];
                    $cards[$i + 1] = $card;
                }
            }
        }
        $bz = true;
        $th = true;
        $sz = true;
        $dz = false;
        $color_flag = '';
        $figure_flag = 0;
        $typefigure = 99; // @2017 类型牌点,0-12,99为空
        $typecolor = 9; // @2017 类型花色,0-3,9为空
        $figures = array(); // 牌点
        $colors = array(); // 花色
        foreach ($cards as $key => $card) {
            $figures[$key] = $figure = $this->figures[$card[1]];
            $colors[$key] = $color = $this->colors[$card[0]];
            if ($key) { // 从第二张牌开始判断
                // 同花
                if ($color != $color_flag) {
                    $th = false; // 如果包含花色不一样，则不是同花
                }
                // 豹子
                if ($figure != $figure_flag) {
                    $bz = false; // 如果包含牌点不一样，则不是豹子
                }
                // 对子
                if ($figure == $figure_flag) {
                    $typefigure = $figure;
                    $dz = true; // 如果包含牌点一样，则是对子
                }
                // 顺子
                if ($figures[$key - 1] - 1 != $figure) {
                    $sz = false; // 当前牌点不等于前一张牌牌点减1则不是顺子
                }
            }
            $color_flag = $color;
            $figure_flag = $figure;
            $typecolor = $color;
        }
        // 顺子
        if ($figures == array(12, 1, 0)) {
            $figures = array(1, 0, -1);
            $sz = true;
        }
        // 对子
        $dz = $dz && !$bz;
        // 同花顺
        $ths = $th && $sz;
        if (!$bz && !$dz) {
            $typefigure = 99;
        }
        if (!$th) {
            $typecolor = 9;
        }
        // 牌型
        // $type = 'dp';
        $type = 5; // 牌型,0-5
        foreach ($this->type as $key => $value) {
            if (isset($$key) && $$key) {
                $type = $this->type[$key];
                break;
            }
        }
        if ($type == 5) {
            $typefigure = 99;
            $typecolor = 9;
        }
        //return compact('type', 'figures', 'colors'); // @2017 多增加返回typecolor和typefigure
        return compact('type', 'figures', 'colors', 'typecolor', 'typefigure');
    }

    /**
     * 选牌
     * @param integer $number 牌的数量
     * @return array 返回三张牌[花色,牌点]
     */
    public function pickCards($number = 3) {
        if (empty($this->cards)) {
            $this->shuffleCards();
        }
        if (sizeof($this->cards) < $number) {
            return false;
        }
        $cards = array();
        for ($i = 0; $i < $number; $i++) {
            $cards[] = array_shift($this->cards);
        }
        return $cards;
    }

    /**
     * @2017 选牌，并指定牌型
     * @param integer $number 牌的数量
     * @return array 返回三张牌[花色,牌点]
     */
    public function pickCardsByType($number = 3, $type = -1) {
        if (empty($this->cards)) {
            $this->shuffleCards();
        }
        if (sizeof($this->cards) < $number) {
            return false;
        }
        $cards = $this->cards;
        $outcards = [];
        $count = count($cards); // 总牌数
        switch ($type) {
            case 0: // 0,三条：三张牌点相同，花色不计，三张A最大
                $typecolors = array_keys($this->colors);
                shuffle($typecolors);
                $typefigurevalue = array_rand($this->figures);
                while ($number > 0) {
                    $i = array_search([array_shift($typecolors), $typefigurevalue], $cards);
                    $number--;
                    $outcards[] = $cards[$i];
                    array_splice($cards, $i, 1); // 取出牌
                }
                break;
            case 1: // 1,同花顺：三张相同花色的顺子
                $typefigure = rand(0, 10);
                $typecolorvalue = array_rand($this->colors);
                while ($number > 0) {
                    $typefigurevalue = array_search($typefigure, $this->figures);
                    $i = array_search([$typecolorvalue, $typefigurevalue], $cards);
                    $typefigure++;
                    $number--;
                    $outcards[] = $cards[$i];
                    array_splice($cards, $i, 1); // 取出牌
                }
                break;
            case 2: // 2,同花：三张花色相同，牌点不计
                $typefigures = array_keys($this->figures);
                shuffle($typefigures);
                $typecolorvalue = array_rand($this->colors);
                while ($number > 0) {
                    $i = array_search([$typecolorvalue, array_shift($typefigures)], $cards);
                    $number--;
                    $outcards[] = $cards[$i];
                    array_splice($cards, $i, 1); // 取出牌
                }
                break;
            case 3: // 3,顺子：花色不同的顺子
                $typecolors = array_keys($this->colors);
                shuffle($typecolors);
                $typefigure = rand(0, 10);
                while ($number > 0) {
                    $typefigurevalue = array_search($typefigure, $this->figures);
                    $i = array_search([array_shift($typecolors), $typefigurevalue], $cards);
                    $typefigure++;
                    $number--;
                    $outcards[] = $cards[$i];
                    array_splice($cards, $i, 1); // 取出牌
                }
                break;
            case 4: // 4,对子：1对牌点相同+1单张
                $typecolors = array_keys($this->colors);
                shuffle($typecolors);
                $typefigurevalue = array_rand($this->figures);
                while ($number > 1) {
                    $i = array_search([array_shift($typecolors), $typefigurevalue], $cards);
                    $number--;
                    $outcards[] = $cards[$i];
                    array_splice($cards, $i, 1); // 取出牌
                }
                $outcards[] = array_shift($cards);
                break;
            default:
                while ($number > 0) {
                    $outcards[] = array_shift($cards);
                    $number--;
                }
                break;
        }
        $this->cards = $cards;
        return $outcards;
    }

    // @2017
    protected static function debugmsg($location, $msg) {
        $content = date('Y-m-d H:i:s') . PHP_EOL . $location . PHP_EOL . (is_string($msg) ? $msg : var_export($msg, true)) . PHP_EOL . PHP_EOL;
        $dir = APP_ROOT_PATH . 'java/logs/debug/';
        if (!is_dir($dir)) {
            mkdir($dir);
        }
        $path = $dir . 'debug-' . date('mdH') . '.txt';
        file_put_contents($path, $content, FILE_APPEND);
    }

    /**
     * 牌组大小
     * @param $player1
     * @param $player2
     * @return bool
     */
    public function compare($player1, $player2) {
        $type1 = $player1['check']['type'];
        $type2 = $player2['check']['type'];
        if ($type1 != $type2) {
            return $type1 < $type2;
        }
        $figure1 = $player1['check']['figures'];
        $figure2 = $player2['check']['figures'];
        $color1 = $player1['check']['colors'];
        $color2 = $player2['check']['colors'];

        switch ($type1) {
            case 0: // bz(豹子/三条)
                return $figure1[0] > $figure2[0];
                break;
            case 1: // ths(同花顺)
            case 3: // sz(顺子)
                if ($figure1 == $figure2) {
                    return $color1[0] < $color2[0];
                } else {
                    return $figure1[0] > $figure2[0];
                }
                break;
            case 4: // dz(对子)
                $dui1 = $figure1[1];
                $dui2 = $figure2[1];
                if ($dui1 != $dui2) {
                    return $dui1 > $dui2;
                }
                $dui1 = array_sum($figure1) - $dui1;
                $dui2 = array_sum($figure2) - $dui2;
                if ($dui1 == $dui2) {
                    $i1 = $figure1[0] == $dui1 ? 2 : 0;
                    $i2 = $figure2[0] == $dui2 ? 2 : 0;
                    return $color1[$i1] < $color2[$i2];
                } else {
                    return $dui1 > $dui2;
                }
                break;
            case 2: // ths(同花顺)
            default: // dp(单牌)
                foreach ($figure1 as $key => $value) {
                    if ($value != $figure2[$key]) {
                        return $value > $figure2[$key];
                    }
                }
                return $color1[0] < $color2[0];
                break;
        }
    }

    /**
     * 游戏执行
     * @param int $number 几组游戏(炸金花是3组,红黑大战是2组)
     * @return array [cards,check] 返回n组牌的信息，含比较结果信息
     */
    public function play($number = 3) {
        if ($number < 2 || $number > 17) {
            $number = 2;
        }
        $this->shuffleCards(); // 洗牌
        $players = array();
        for ($i = 0; $i < $number; $i++) {
            $cards = $this->pickCards(); // 选牌(默认3张)[花色,牌点]
            $players[] = array(
                'cards' => $cards, // 选好的牌[花色,牌点]
                'check' => $this->checkCards($cards), // 牌类型(牌类型,牌点,花色)
            );
        }
        return $this->order($players);
    }

    /**
     * 游戏执行
     * @param int $number 几组游戏(炸金花是3组,红黑大战是2组)
     * @return array [cards,check] 返回n组牌的信息，含比较结果信息
     */
    public function playForHonghei($number = 2, $type = -1) {
        if ($number < 2 || $number > 17) {
            $number = 2;
        }
        $this->shuffleCards(); // 洗牌
        $players = array();
        for ($i = 0; $i < $number; $i++) {
            if ($type > -1 && $i < 1) {
                $cards = $this->pickCardsByType(3, $type); // @2017 设定牌为指定牌型
            } else {
                $cards = $this->pickCards(); // 选牌(默认3张)[花色,牌点]
            }
            //self::debugmsg('HongHei.class.php::playForHonghei()[' . $type . ',' . $i . '.0]', $cards);
            $players[] = array(
                'cards' => $cards, // 选好的牌[花色,牌点]
                'check' => $this->checkCards($cards), // 牌类型(牌类型,牌点,花色)
            );
        }
        return $this->order($players);
    }

    /**
     * 比较n组牌的大小，并按大小排序
     * @param type $players
     * @return type 返回n组牌的信息，含比较结果信息
     */
    public function order($players) {
        $max = sizeof($players) - 1;
        for ($j = 0; $j < $max; $j++) {
            for ($i = 0; $i < $max - $j; $i++) {
                if ($this->compare($players[$i + 1], $players[$i])) {
                    $player = $players[$i];
                    $players[$i] = $players[$i + 1];
                    $players[$i + 1] = $player;
                }
            }
        }
        return $players;
    }

    /**
     * 排列组合
     * @param type $array
     * @param type $number
     * @return array
     */
    protected static function combination($array, $number) {
        $r = array();
        $n = count($array);
        if ($number <= 0 || $number > $n) {
            return $r;
        }
        for ($i = 0; $i < $n; $i++) {
            $t = array($array[$i]);
            if ($number == 1) {
                $r[] = $t;
            } else {
                $b = array_slice($array, $i + 1);
                $c = self::combination($b, $number - 1);
                foreach ($c as $v) {
                    $r[] = array_merge($t, $v);
                }
            }
        }
        return $r;
    }

}
